package com.jgp.sys.service;

import com.jgp.common.pojo.TreeBean;
import com.jgp.common.utils.FileUtil;
import com.jgp.common.utils.JGPUtil;
import com.jgp.sys.model.Menu;
import com.jgp.sys.query.Operator;
import com.jgp.sys.query.OrderDirection;
import com.jgp.sys.query.OrderList;
import com.jgp.sys.query.QueryFilterList;
import com.jgp.sys.repository.MenuRepository;
import com.jgp.sys.ui.Pager;
import com.jgp.sys.utils.TreeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目   sys
 * 作者   loufei
 * 时间   2018/3/4
 */
@Slf4j
@Service
@CacheConfig(cacheNames = "MENU-2-SERVICE")
public class MenuService {
    
    static List<Menu> PLATFORM_MENUS;
    
    @Autowired
    private MenuRepository repository;
    
    /**
     * 处理平台菜单
     *
     * @return
     */
    public void dealPlatformMenus() {
        List<Menu> allMenus = new ArrayList<>();
        
        try {
            Resource[] resources = FileUtil.scanAllMenuJsonFile();
            for (Resource resource : resources) {
                List<Menu> menus = new ArrayList<>();
                
                for (Object menu : Objects.requireNonNull(JGPUtil.toBean(FileUtil.inputStream2Str(resource.getInputStream()), List.class))) {
                    Map map = (Map) menu;
                    menus.add(JGPUtil.toBean(map, Menu.class));
                }
                
                allMenus.addAll(menus);
            }
        } catch (IOException e) {
            log.error("读取平台内置菜单失败!", e);
        }
        log.debug("平台菜单处理完毕！");
        PLATFORM_MENUS = allMenus.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Menu::getCode))), ArrayList::new));
    }
    
    @Cacheable(key = "'TREE:with-out-platform'")
    public List<TreeBean> queryTreeWidthOutPlatform() {
        List<Menu> menus = query(null, null, null, null);
        return TreeUtil.createTree(menus);
    }
    
    /**
     * 【缓存】
     * 根据类型缓存树
     *
     * @param type
     * @return
     */
    @Cacheable(key = "'TREE:'+#type")
    public List<TreeBean> queryTreeByTypeFromCache(String type) {
        return queryTreeByType(type);
    }
    
    @Cacheable(key = "'LIST-ALL'")
    public List<Menu> queryAllMenuFromCache() {
        return repository.readAll();
    }
    
    /**
     * 【缓存】
     * 清除缓存
     */
    @Caching(evict = {@CacheEvict(key = "'TREE:*'", allEntries = true), @CacheEvict(key = "'LIST-ALL'", allEntries = true)})
    public void removeCache() { }
    
    /**
     * 创建/更新菜单项
     *
     * @param menu
     * @return
     */
    @Transactional
    public Menu save(Menu menu) {
        if(StringUtils.isNotBlank(menu.getSupers())&&Objects.nonNull(menu.getId())){
            Menu persist = repository.read(menu.getId());
            if(!persist.getType().equals(menu.getType())){
                QueryFilterList filters = new QueryFilterList();
                filters.addFilter("supers",Operator.likeL,menu.getSupers()+","+menu.getId());
                List<Menu> menus = repository.readPersist(filters);
                menus.forEach(m->{
                    m.setType(menu.getType());
                });
                repository.updateInBatch(menus);
            }
        }
        return repository.createOrUpdate(menu);
    }
    
    /**
     * 查询整个树
     *
     * @return
     */
    public List<TreeBean> queryFullTree() {
        return TreeUtil.createTree(queryFull());
    }
    
    public List<Menu> queryFull() {
        List<Menu> menus = new ArrayList<>();
        menus.addAll(queryAllMenuFromCache());
        menus.addAll(PLATFORM_MENUS);
        menus = menus.stream().distinct().sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());
        return menus;
    }
    
    /**
     * 根据类型查询
     *
     * @param type
     * @return
     */
    public List<TreeBean> queryTreeByType(String type) {
        return TreeUtil.createTree(query(null, null, null, type));
    }
    
    /**
     * 根据条件查询菜单项
     *
     * @param parentId 父级ID【EQ】
     * @param title    标题【LIKE】
     * @param code     编码【EQ】
     * @param type     类型【EQ】
     * @return
     */
    public List<Menu> query(String parentId, String title, String code, String type) {
        return query(parentId, title, code, type, null);
    }
    
    /**
     * 【分页】
     * 根据条件查询菜单项
     *
     * @param parentId 父级ID【EQ】
     * @param title    标题【LIKE】
     * @param code     编码【EQ】
     * @param type     类型【EQ】
     * @param pager    分页
     * @return
     */
    public List<Menu> query(String parentId, String title, String code, String type, Pager pager) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilters(filters(parentId, title, code, type));
        OrderList orders = new OrderList();
        orders.addOrder("sort", "ASC");
        return repository.read(filters, orders, pager);
    }
    
    public Menu queryOne(Long id) {
        return repository.read(id);
    }
    
    /**
     * 构建条件过滤器
     *
     * @param parentId
     * @param title
     * @param code
     * @param type
     * @return
     */
    private QueryFilterList filters(String parentId, String title, String code, String type) {
        QueryFilterList filters = new QueryFilterList();
        if (StringUtils.isNotBlank(title)) filters.addFilter("title", Operator.like, title);
        if (StringUtils.isNotBlank(code)) filters.addFilter("code", Operator.eq, code);
        if (StringUtils.isNotBlank(type)) filters.addFilter("type", Operator.eq, type);
        if (StringUtils.isNotBlank(parentId)) {
            filters.addFilter("parentId", Operator.eq, parentId);
        }
        return filters;
    }
    
    public List<TreeBean> queryTreeLazy(String parentId, String type) {
        return TreeUtil.createTree(query(parentId, null, null, type));
    }
    
    @Transactional
    public void removeMenu(List<Long> ids) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("id", Operator.in, ids);
        repository.deleteTreeItemAndAllChildren(filters);
    }
    
    @Transactional
    public void removeMenu(Long id) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("id", Operator.eq, id);
        repository.deleteTreeItemAndAllChildren(filters);
    }
    
    public List<Menu> queryForEndPoint(List<Long> ids, String type) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("id", Operator.in, ids);
        filters.addFilter("type", Operator.eq, type);
        return repository.read(filters);
    }
    
    /**
     * 查询菜单
     *
     * @param ids          菜单ID集合
     * @param endPointType 菜单类型 PAD MOBILE PC
     * @param weight       权重
     * @return
     */
    public List<Menu> queryMenu(List<Long> ids, String endPointType, Integer weight) {
        if (Objects.isNull(ids) || ids.size() == 0) {
            return new ArrayList<>();
        } else {
            QueryFilterList filters = new QueryFilterList();
            filters.addFilter("id", Operator.in, ids);
            if (StringUtils.isNotBlank(endPointType)) filters.addFilter("type", Operator.eq, endPointType);
            filters.addFilter("weight", Operator.le, weight);
            OrderList orders = new OrderList();
            orders.addOrder("sort", OrderDirection.ASC);
            return repository.read(filters,orders);
        }
    }
    
    public List<Menu> querySysAdminMenu(Integer weight) {
        QueryFilterList filters = new QueryFilterList();
        filters.addFilter("weight", Operator.le, weight);
        OrderList orders = new OrderList();
        orders.addOrder("sort", OrderDirection.ASC);
        return repository.read(filters,orders);
    }
}
